package com.yancloud.android.contractclient;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.os.IBinder;
import android.util.Base64;
import android.util.Log;


import java.io.ByteArrayOutputStream;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.reflect.TypeToken;
import com.yancloud.android.manager.IAppConnector;
import com.yancloud.android.manager.PackageVersionPair;
import com.yancloud.android.reflection.get.CMDManager;


import ias.deepsearch.com.helper.service.DaemonService;
import ias.deepsearch.com.helper.util.ShellUtils;
import ias.deepsearch.com.helper.util.normal.FileUtil;
import io.netty.handler.codec.base64.Base64Encoder;


public class CMClientController  {
	public static SyncResult sync = new SyncResult();
	static Gson gson = new Gson();
 	String nodeID;
	Map<String, Set<String>> subscribedInfo;
 	public String contractDesp;
	CMClientHandler handler;

	public CMClientController(String nodeID) {
		subscribedInfo = new HashMap<>();
		this.nodeID = nodeID;
	}
	public void initHandler(CMClientHandler h){
		handler = h;
	}
	int udpID = (int)(Math.random()*100000);
	@Action
	public void getUDPID(JsonObject jo, ResultCallback result) {
		Map<String, String> ret = new HashMap<>();
		ret.put("action", "onGetUDPID");
		ret.put("udpID", udpID+"");

		result.onResult(new Gson().toJson(ret));
	}

	public static class Response {
		String responseID;
		String action;
		Object data;
		long executeTime;
	}

	// ==========Handler
	@Action
	private void ping(JsonObject json, ResultCallback resultCallback) {
		resultCallback.onResult("{\"action\":\"pong\",\"data\":" + System.currentTimeMillis() + "}");
	}

	@Action
	public void syncPong(JsonObject json, ResultCallback resultCallback) {
		sync.wakeUp(json.get("requestID").getAsString(), "{\"status\":\"Success\",\"result\":\"a\"}");
	}
	public boolean syncPing() {
		try {
			Map<String, String> syncPing = new HashMap<>();
			syncPing.put("action", "syncPing");
			String requestID = System.currentTimeMillis() + "_" + (int) (Math.random() * 10000);
			syncPing.put("requestID", requestID);
			handler.sendMsg(gson.toJson(syncPing));
			ContractResult result = sync.syncSleep(requestID);
			return result != null && result.status == ContractResult.Status.Success;
		} catch (Exception e) {
			return false;
		}
	}


	// from nodecenter to cmclient
	@Action
	public void subscribLocalEvent(JsonObject json, ResultCallback resultCallback) {

	}

	public void listCMInfo(ResultCallback bc) {
		bc.onResult("{\"action\":\"listCMInfo\"}");
	}

	@Action
	public void onSetNodeID(JsonObject json, ResultCallback resultCallback) {
		listCMInfo(resultCallback);
		updateContract(resultCallback);
	}

	@SuppressWarnings("serial")
	@Action
	public void onListCMInfo(JsonObject json, ResultCallback resultCallback) {

	}
	private static String getContractDesp(){
		try {
			IBinder binder = DaemonService.instance.onBind(null);
			List<PackageVersionPair> list = IAppConnector.Stub.asInterface(binder).listPackages(); //列出本地有多少可用接口
			List<ContractDesp> info = new ArrayList<>();
			ContractDesp de = new ContractDesp();
			de.contractName = de.contractID = "CMManager";
			FunctionDesp fun2 = new FunctionDesp("adbCmd",null);
			de.exportedFunctions = new ArrayList<>();
			de.exportedFunctions.add(fun2);
			info.add(de);
			for (PackageVersionPair pair : list) {
				try {
					List<APIDescription> functions = new Gson().fromJson(
							CMDManager.forward(pair.pkgName, "getMethodDescriptions", "AppPorter"),
							new TypeToken<List<APIDescription>>() {
							}.getType());
					ContractDesp desp = new ContractDesp();
					info.add(desp);
					desp.contractID = pair.pkgName;
					desp.contractName = pair.pkgName;
					desp.events = new ArrayList<>();
					desp.exportedFunctions = new ArrayList<>();
					for (APIDescription apiDesp : functions) {
						FunctionDesp fun = new FunctionDesp(apiDesp.mName, null);
						desp.exportedFunctions.add(fun);
					}
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			return gson.toJson(info);
		}catch(Exception e){
			e.printStackTrace();
		}
		return null;
	}
	public void updateContract(ResultCallback resultCallback) {
		try {
			String curr = getContractDesp();
			if (contractDesp == null || contractDesp.equals(curr)){
				contractDesp = curr;
			JsonObject jo = new JsonObject();
			jo.addProperty("action", "updateContract");
			jo.addProperty("contracts", contractDesp);
				resultCallback.onResult(gson.toJson(jo));}
		}catch(Exception e){
			e.printStackTrace();;
		}
	}

	// subscribe event in server
	// TODO invoke this method when subscribe in ContractManager?
	private String subEvent(String subscriberID, String contractIDOrName, String event) {
		return "contractID " + contractIDOrName + " not exists";
	}

	// local event occurs, we need use subscriber in ContractManager;


	// events from server
	@Action(async = true)
	public void onReceiveEvent(JsonObject jo, ResultCallback resultCallback) {

	}

	@Action(async = true)
	public void executeContractLocally(JsonObject jo, ResultCallback resultCallback) {
		String requestID = jo.get("requestID").getAsString();
		String requesterNodeID = jo.get("requesterNodeID").getAsString();
		String crStr = jo.get("contractRequest").getAsString();
		ContractRequest contractRequest = gson.fromJson(crStr, ContractRequest.class);
		String cr = CMDManager.handle(contractRequest.contractID,contractRequest.action, contractRequest.arg);
		ContractResult cresult = new ContractResult(ContractResult.Status.Success,cr);
		JsonObject ret = new JsonObject();
		ret.addProperty("action", "onReceiveContractExecution");
		ret.addProperty("requestID", requestID);
		ret.addProperty("requesterNodeID", requesterNodeID);
		ret.addProperty("contractResult", gson.toJson(cresult));
		resultCallback.onResult(gson.toJson(ret));
	}

	@Action(async = true)
	public void onReceiveContractExecution(JsonObject jo, ResultCallback resultCallback) {

	}

	@Action(async = true)
	public void takeScreenshot(JsonObject jo, ResultCallback resultCallback){
		long startTime = System.currentTimeMillis();
		String fileName = "st_"+System.currentTimeMillis()+".png";
		File dir = new File("/sdcard/screenshot/");
		if (!dir.exists())
			dir.mkdirs();
		ShellUtils.execCommand("screencap -p /sdcard/screenshot/"+fileName, true);
		//byte[] pics = FileUtil.getBytes("/sdcard/screenshot/"+fileName);
		long afterDump = System.currentTimeMillis();

		Bitmap bitmap = BitmapFactory.decodeFile("/sdcard/screenshot/"+fileName);
		Matrix matrix = new Matrix();
		matrix.postScale(0.25F,0.25F);
		bitmap = bitmap.createBitmap(bitmap,0,0,bitmap.getWidth(),bitmap.getHeight(),matrix,true);
		ByteArrayOutputStream stream = new ByteArrayOutputStream();
		bitmap.compress(Bitmap.CompressFormat.PNG,80,stream);
		long afterZip = System.currentTimeMillis();

		String b64 = Base64.encodeToString(stream.toByteArray(),Base64.DEFAULT);
		long afterEncode = System.currentTimeMillis();
		Log.d("[CMClientController]",(afterDump-startTime)+" "+(afterZip-afterDump)+ " "+(afterEncode-afterZip));
		Map<String,String> ret = new HashMap<>();
		ret.put("action","onTakeScreenshot");
		ret.put("data",b64);
		ret.put("responseID",jo.get("requestID").getAsString());
		resultCallback.onResult(new Gson().toJson(ret));
	}






	// ===Nodecenter calls
	@Action(async = true)
	public void getLogSize(JsonObject json, ResultCallback rc) {

	}

	@Action(async = true)
	public void requestLog(JsonObject json, ResultCallback rc) {

	}

	@Action(async = true)
	public void requestLastLog(JsonObject json, ResultCallback rc) {

	}

}
